/* Copyright (c) 2023, Canaan Bright Sight Co., Ltd
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include "sys/ioctl.h"

#define HASH_DEVICE_NAME "/dev/hash"
#define RT_HASH_INIT _IOWR('H', 0, int)
#define RT_HASH_UPDATE _IOWR('H', 1, int)
#define RT_HASH_FINAL _IOWR('H', 2, int)

typedef enum {
    SHA_224,
    SHA_256,
    SHA_384,
    SHA_512,
    SHA_512_224,
    SHA_512_256,
    SM3,
} pufs_hash_t;

union rt_hash_control_args {
    struct {
        uint8_t mode;
    } init;
    struct {
        uint8_t* msg;
        uint32_t msglen;
    } update;
    struct {
        uint8_t* dgst;
        uint32_t* dlen;
    } final;
};

static const struct hash_test_pattern {
    pufs_hash_t mode;
    uint32_t msglen;
    const void* msg;
    const void* md;
} hash_tp[] = {
    {
        SHA_224,
        0,
        NULL,
        "\xd1\x4a\x02\x8c\x2a\x3a\x2b\xc9\x47\x61\x02\xbb\x28\x82\x34\xc4\x15\xa2\xb0\x1f\x82\x8e\xa6\x2a\xc5\xb3\xe4\x2f",
    },
    {
        SHA_224,
        163,
        "\xf1\x49\xe4\x1d\x84\x8f\x59\x27\x6c\xfd\xdd\x74\x3b\xaf\xa9\xa9\x0e\x1e\xe4\xa2\x63\xa1\x18\x14\x2b\x33\xe3\x70\x21\x76\xef\x0a\x59\xf8\x23\x7a\x1c\xb5\x1b\x42\xf3\xde\xd6\xb2\x02\xd9\xaf\x09\x97\x89\x8f\xdd\x03\xcf\x60\xbd\xa9\x51\xc5\x14\x54\x7a\x08\x50\xce\xc2\x54\x44\xae\x2f\x24\xcb\x71\x1b\xfb\xaf\xcc\x39\x56\xc9\x41\xd3\xde\x69\xf1\x55\xe3\xf8\xb1\x0f\x06\xdb\x5f\x37\x35\x9b\x77\x2d\xdd\x43\xe1\x03\x5a\x0a\x0d\x3d\xb3\x32\x42\xd5\x84\x30\x33\x83\x3b\x0d\xd4\x3b\x87\x0c\x6b\xf6\x0e\x8d\xea\xb5\x5f\x31\x7c\xc3\x27\x3f\x5e\x3b\xa7\x47\xf0\xcb\x65\x05\x0c\xb7\x22\x87\x96\x21\x0d\x92\x54\x87\x36\x43\x00\x8d\x45\xf2\x9c\xfd\x6c\x5b\x06\x0c\x9a",
        "\x9d\xb6\xdc\x3a\x23\xab\xd7\xb6\xc3\xd7\x2c\x38\xf4\x84\x3c\x7d\xe4\x8a\x71\xd0\xba\x91\xa8\x6b\x18\x39\x3e\x5f",
    },
    {
        SHA_256,
        0,
        NULL,
        "\xe3\xb0\xc4\x42\x98\xfc\x1c\x14\x9a\xfb\xf4\xc8\x99\x6f\xb9\x24\x27\xae\x41\xe4\x64\x9b\x93\x4c\xa4\x95\x99\x1b\x78\x52\xb8\x55",
    },
    {
        SHA_256,
        163,
        "\x45\x11\x01\x25\x0e\xc6\xf2\x66\x52\x24\x9d\x59\xdc\x97\x4b\x73\x61\xd5\x71\xa8\x10\x1c\xdf\xd3\x6a\xba\x3b\x58\x54\xd3\xae\x08\x6b\x5f\xdd\x45\x97\x72\x1b\x66\xe3\xc0\xdc\x5d\x8c\x60\x6d\x96\x57\xd0\xe3\x23\x28\x3a\x52\x17\xd1\xf5\x3f\x2f\x28\x4f\x57\xb8\x5c\x8a\x61\xac\x89\x24\x71\x1f\x89\x5c\x5e\xd9\x0e\xf1\x77\x45\xed\x2d\x72\x8a\xbd\x22\xa5\xf7\xa1\x34\x79\xa4\x62\xd7\x1b\x56\xc1\x9a\x74\xa4\x0b\x65\x5c\x58\xed\xfe\x0a\x18\x8a\xd2\xcf\x46\xcb\xf3\x05\x24\xf6\x5d\x42\x3c\x83\x7d\xd1\xff\x2b\xf4\x62\xac\x41\x98\x00\x73\x45\xbb\x44\xdb\xb7\xb1\xc8\x61\x29\x8c\xdf\x61\x98\x2a\x83\x3a\xfc\x72\x8f\xae\x1e\xda\x2f\x87\xaa\x2c\x94\x80\x85\x8b\xec",
        "\x3c\x59\x3a\xa5\x39\xfd\xcd\xae\x51\x6c\xdf\x2f\x15\x00\x0f\x66\x34\x18\x5c\x88\xf5\x05\xb3\x97\x75\xfb\x9a\xb1\x37\xa1\x0a\xa2",
    },
    {
        SHA_384,
        0,
        NULL,
        "\x38\xb0\x60\xa7\x51\xac\x96\x38\x4c\xd9\x32\x7e\xb1\xb1\xe3\x6a\x21\xfd\xb7\x11\x14\xbe\x07\x43\x4c\x0c\xc7\xbf\x63\xf6\xe1\xda\x27\x4e\xde\xbf\xe7\x6f\x65\xfb\xd5\x1a\xd2\xf1\x48\x98\xb9\x5b",
    },
    {
        SHA_384,
        227,
        "\x62\xc6\xa1\x69\xb9\xbe\x02\xb3\xd7\xb4\x71\xa9\x64\xfc\x0b\xcc\x72\xb4\x80\xd2\x6a\xec\xb2\xed\x46\x0b\x7f\x50\x01\x6d\xda\xf0\x4c\x51\x21\x87\x83\xf3\xaa\xdf\xdf\xf5\xa0\x4d\xed\x03\x0d\x7b\x3f\xb7\x37\x6b\x61\xba\x30\xb9\x0e\x2d\xa9\x21\xa4\x47\x07\x40\xd6\x3f\xb9\x9f\xa1\x6c\xc8\xed\x81\xab\xaf\x8c\xe4\x01\x6e\x50\xdf\x81\xda\x83\x20\x70\x37\x2c\x24\xa8\x08\x90\xaa\x3a\x26\xfa\x67\x57\x10\xb8\xfb\x71\x82\x66\x24\x9d\x49\x6f\x31\x3c\x55\xd0\xba\xda\x10\x1f\x8f\x56\xee\xcc\xee\x43\x45\xa8\xf9\x8f\x60\xa3\x66\x62\xcf\xda\x79\x49\x00\xd1\x2f\x94\x14\xfc\xbd\xfd\xeb\x85\x38\x8a\x81\x49\x96\xb4\x7e\x24\xd5\xc8\x08\x6e\x7a\x8e\xdc\xc5\x3d\x29\x9d\x0d\x03\x3e\x6b\xb6\x0c\x58\xb8\x3d\x6e\x8b\x57\xf6\xc2\x58\xd6\x08\x1d\xd1\x0e\xb9\x42\xfd\xf8\xec\x15\x7e\xc3\xe7\x53\x71\x23\x5a\x81\x96\xeb\x9d\x22\xb1\xde\x3a\x2d\x30\xc2\xab\xbe\x0d\xb7\x65\x0c\xf6\xc7\x15\x9b\xac\xbe\x29\xb3\xa9\x3c\x92\x10\x05\x08",
        "\x07\x30\xe1\x84\xe7\x79\x55\x75\x56\x9f\x87\x03\x02\x60\xbb\x8e\x54\x49\x8e\x0e\x5d\x09\x6b\x18\x28\x5e\x98\x8d\x24\x5b\x6f\x34\x86\xd1\xf2\x44\x7d\x5f\x85\xbc\xbe\x59\xd5\x68\x9f\xc4\x94\x25",
    },
    {
        SHA_512,
        0,
        NULL,
        "\xcf\x83\xe1\x35\x7e\xef\xb8\xbd\xf1\x54\x28\x50\xd6\x6d\x80\x07\xd6\x20\xe4\x05\x0b\x57\x15\xdc\x83\xf4\xa9\x21\xd3\x6c\xe9\xce\x47\xd0\xd1\x3c\x5d\x85\xf2\xb0\xff\x83\x18\xd2\x87\x7e\xec\x2f\x63\xb9\x31\xbd\x47\x41\x7a\x81\xa5\x38\x32\x7a\xf9\x27\xda\x3e",
    },
    {
        SHA_512,
        227,
        "\x4f\x05\x60\x09\x50\x66\x4d\x51\x90\xa2\xeb\xc2\x9c\x9e\xdb\x89\xc2\x00\x79\xa4\xd3\xe6\xbc\x3b\x27\xd7\x5e\x34\xe2\xfa\x3d\x02\x76\x85\x02\xbd\x69\x79\x00\x78\x59\x8d\x5f\xcf\x3d\x67\x79\xbf\xed\x12\x84\xbb\xe5\xad\x72\xfb\x45\x60\x15\x18\x1d\x95\x87\xd6\xe8\x64\xc9\x40\x56\x4e\xaa\xfb\x4f\x2f\xea\xd4\x34\x6e\xa0\x9b\x68\x77\xd9\x34\x0f\x6b\x82\xeb\x15\x15\x88\x08\x72\x21\x3d\xa3\xad\x88\xfe\xba\x9f\x4f\x13\x81\x7a\x71\xd6\xf9\x0a\x1a\x17\xc4\x3a\x15\xc0\x38\xd9\x88\xb5\xb2\x9e\xdf\xfe\x2d\x6a\x06\x28\x13\xce\xdb\xe8\x52\xcd\xe3\x02\xb3\xe3\x3b\x69\x68\x46\xd2\xa8\xe3\x6b\xd6\x80\xef\xcc\x6c\xd3\xf9\xe9\xa4\xc1\xae\x8c\xac\x10\xcc\x52\x44\xd1\x31\x67\x71\x40\x39\x91\x76\xed\x46\x70\x00\x19\xa0\x04\xa1\x63\x80\x6f\x7f\xa4\x67\xfc\x4e\x17\xb4\x61\x7b\xbd\x76\x41\xaa\xff\x7f\xf5\x63\x96\xba\x8c\x08\xa8\xbe\x10\x0b\x33\xa2\x0b\x5d\xaf\x13\x4a\x2a\xef\xa5\xe1\xc3\x49\x67\x70\xdc\xf6\xba\xa4\xf7\xbb",
        "\xa9\xdb\x49\x0c\x70\x8c\xc7\x25\x48\xd7\x86\x35\xaa\x7d\xa7\x9b\xb2\x53\xf9\x45\xd7\x10\xe5\xcb\x67\x7a\x47\x4e\xfc\x7c\x65\xa2\xaa\xb4\x5b\xc7\xca\x11\x13\xc8\xce\x0f\x3c\x32\xe1\x39\x9d\xe9\xc4\x59\x53\x5e\x88\x16\x52\x1a\xb7\x14\xb2\xa6\xcd\x20\x05\x25",
    },
    {
        SHA_512_224,
        0,
        NULL,
        "\x6e\xd0\xdd\x02\x80\x6f\xa8\x9e\x25\xde\x06\x0c\x19\xd3\xac\x86\xca\xbb\x87\xd6\xa0\xdd\xd0\x5c\x33\x3b\x84\xf4",
    },
    {
        SHA_512_224,
        227,
        "\x96\x25\xae\x61\x8e\xa6\x33\xfd\x7a\xe5\xb2\x0c\xea\xfd\x6b\x1f\x3a\xb1\xa6\xaa\x20\xad\xed\x66\x81\x0e\x78\xf3\x89\x25\xe9\xc2\xfa\x78\x3a\x32\xc4\x0a\xf3\xf9\xd7\xdd\xa0\xc6\x35\xb4\x82\x25\x4b\x1d\x85\xa2\x81\xaf\x72\x31\x10\x91\x66\xcd\x13\x3c\x83\x60\xe2\x81\xe5\xe3\x9b\xcd\xd7\xc6\x01\xac\x47\x92\x8a\x8c\x78\xcd\xb3\xc4\xf7\x1e\x97\xd4\xd0\xb1\xc0\xee\x01\xdd\x3d\xb6\x2f\x04\xf4\x47\x98\xbb\x3a\x76\x49\x2b\xa1\x5a\x91\xb7\x11\x0c\xb5\xe0\x1b\xab\xe5\x65\x89\xa3\x6f\xae\x3a\x2f\x33\x6a\x2d\x1d\x57\x78\xdb\xd2\x3c\x03\xca\x8d\xb0\xf2\x5f\xf0\x65\x7f\xf4\xbc\xa1\x25\x2a\xdc\x38\xc0\x80\xa5\xb8\xf0\x25\x5c\xe3\xbe\x0b\xf8\x62\x82\x3d\x2a\xb7\x04\x72\x9b\x74\xe1\xe2\x75\xaa\x30\x58\x24\xa5\x66\x89\x5e\xd6\x77\xa4\x60\x11\x3e\x2a\x7b\xf9\x1f\x00\xd0\xb8\xeb\xc3\x58\xf3\x03\x5b\x27\xfc\xc1\xd3\xf1\x4a\x13\x67\xcd\x27\x69\xdf\x39\xa9\xd2\x1c\x5e\xe3\x61\xf1\x96\x5c\xd6\x34\x2c\xc1\x7a\x14\x63\xd6",
        "\x72\x64\x0a\x79\xfb\xb1\xcf\xb2\x6e\x09\xb4\xb3\x53\x85\x38\x9e\xd6\x33\xa5\x5e\x09\x29\x06\xd0\x1a\x71\x86\xe1",
    },
    {
        SHA_512_256,
        0,
        NULL,
        "\xc6\x72\xb8\xd1\xef\x56\xed\x28\xab\x87\xc3\x62\x2c\x51\x14\x06\x9b\xdd\x3a\xd7\xb8\xf9\x73\x74\x98\xd0\xc0\x1e\xce\xf0\x96\x7a",
    },
    {
        SHA_512_256,
        227,
        "\x97\xe0\x03\x90\x3b\xb9\x71\xa5\x23\xce\x0c\x82\xbd\xa5\xd6\x73\x3c\x76\xb9\x0d\xeb\x30\x75\x59\xc1\xbd\xdd\x35\x36\x87\x43\xf6\x56\x3b\x31\x52\x14\xcd\x5a\x7e\xe0\xbc\xcf\x93\x7c\x97\x76\x36\x0b\xc0\xb9\x78\x6b\x70\x7b\xfb\xc4\xfb\x50\x57\x61\x55\xed\xbb\xbf\xd5\xdd\xd8\xe4\x3a\x76\xfa\xf2\xec\x0c\x78\xfc\x84\x64\x4f\x18\x8d\x6b\x0a\xb6\x8c\x28\xe5\x30\x3f\xf0\x31\xa2\x23\xd9\xfa\xfb\x38\x71\xe8\x54\x08\xaf\x63\x81\xe6\x29\xfa\xe6\x74\x88\x06\x8c\x68\x39\x8a\x75\x8f\x66\x5e\x2c\x12\x25\x8d\x9f\xf8\xef\xfb\x31\xec\x53\x4b\x0c\x40\xeb\xff\xb4\x33\x90\xe1\xe2\x6f\xca\xa2\x8f\xd6\x8a\xc2\x4f\x7e\x1c\xaf\xe0\xfa\x57\x31\x03\xdc\x17\x05\x8a\x77\xed\xc9\xb3\xea\x14\x18\xb4\x5a\xa7\xf5\x97\x7e\x12\x6d\x48\x61\xc7\x78\xed\x63\x32\x21\x75\x81\xee\xe6\x74\xd7\x39\x62\x2e\x63\xa5\x29\xf1\x0c\x11\xf4\xa9\xe3\xd8\xfe\xae\xa8\x48\xad\xe0\x90\x56\x75\xf6\x45\x8f\xfa\x13\x2f\x52\x74\x9a\xf2\x3d\x58\x44\x38\xe5",
        "\x00\xce\x3b\x59\x2d\x4e\x1a\x65\xf7\x80\xdf\x35\x1f\xa7\xb2\xc0\x1b\x49\xdf\x4e\xa9\x13\xc3\xfa\xb2\x42\x97\xf5\x79\x1b\x18\xe5",
    },
    {
        SM3,
        3,
        "abc",
        "\x66\xc7\xf0\xf4\x62\xee\xed\xd9\xd1\xf2\xd4\x6b\xdc\x10\xe4\xe2\x41\x67\xc4\x87\x5c\xf2\xf7\xa2\x29\x7d\xa0\x2b\x8f\x4b\xa8\xe0",
    },
    {
        SM3,
        64,
        "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd",
        "\xde\xbe\x9f\xf9\x22\x75\xb8\xa1\x38\x60\x48\x89\xc1\x8e\x5a\x4d\x6f\xdb\x70\xe5\x38\x7e\x57\x65\x29\x3d\xcb\xa3\x9c\x0c\x57\x32",
    },
    {
        SM3,
        0,
        NULL,
        "\x1a\xb2\x1d\x83\x55\xcf\xa1\x7f\x8e\x61\x19\x48\x31\xe8\x1a\x8f\x22\xbe\xc8\xc7\x28\xfe\xfb\x74\x7e\xd0\x35\xeb\x50\x82\xaa\x2b",
    },
};

int main(int argc, char* argv[])
{
    int fd;
    int ntests = sizeof(hash_tp) / sizeof(struct hash_test_pattern);
    int i, j;
    uint8_t out[64] = { 0 };
    uint32_t outlen = 0;
    union rt_hash_control_args ctl;

    fd = open(HASH_DEVICE_NAME, O_RDWR);
    if (fd < 0) {
        printf("open /dev/HWhash err!\n");
        return -1;
    }

    for (i = 0; i < ntests; i++) {
        printf("case %d, msglen = %d\n", i, hash_tp[i].msglen);
        ctl.init.mode = hash_tp[i].mode;
        // init
        if (ioctl(fd, RT_HASH_INIT, &ctl)) {
            perror("ioctl");
            close(fd);
            return -1;
        }
        ctl.update.msg = (void*)hash_tp[i].msg;
        ctl.update.msglen = hash_tp[i].msglen;
        // update
        if (ioctl(fd, RT_HASH_UPDATE, &ctl)) {
            perror("ioctl");
            close(fd);
            return -1;
        }
        ctl.final.dgst = (void*)out;
        ctl.final.dlen = &outlen;
        // finish
        if (ioctl(fd, RT_HASH_FINAL, &ctl)) {
            perror("ioctl");
            close(fd);
            return -1;
        }

        if (memcmp(out, (void*)hash_tp[i].md, outlen) == 0) {
            printf("hashlen = %d\n", outlen);
            printf("Success!\n");
        } else {
            printf("Fail!\n");
            printf("golden message digest       computed message digest\n\r");
            for (j = 0; j < outlen; j++)
                printf("0x%x 0x%x\n", *(uint8_t*)(hash_tp[i].md + j), *(uint8_t*)(out + j));
        }

        memset(out, 0, outlen);
    }

    close(fd);

    return 0;
}
